﻿using SuperSocket.Client;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Text;

namespace SuperSocket.Client.HttpCode;

public sealed class HttpRequestItem
{
    private const string _connection = "close"; //Keep-Alive close
    private const string _acceptLanguage = "zh-cn";
    private const string _contentType = "application/x-www-form-urlencoded";
    private const string _userAgent =
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.15 Safari/537.36";

    private readonly WebHeaderCollection _webHeaderCollection = new WebHeaderCollection();

    public HttpRequestItem(string url)
    {
        Uri = new Uri(url);
    }

    public HttpRequestItem(string url, HttpCode.HttpMethod method)
    {
        Uri = new Uri(url);
        Method = method;
    }

    public Uri Uri { get; set; }

    public string Content { get; set; }

    public byte[] Raw { get; set; }

    public Encoding Encoding { get; set; } = Encoding.UTF8;

    public HttpCode.HttpMethod Method { get; set; } = HttpCode.HttpMethod.Get;

    public TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(15);

    public HttpRequestItem AddHeader(string name, string value)
    {
        _webHeaderCollection.Add(name, value);

        return this;
    }

    public HttpRequestItem AddCookie(string cookie)
    {
        const string CookieName = "Cookie";

        _webHeaderCollection.Add(CookieName, cookie);

        return this;
    }

    public HttpRequestItem AddCookies(IEnumerable<KeyValuePair<string, object>> source)
    {
        return AddCookie(source.Aggregate(new StringBuilder(),
            resultSelector: builder => { return builder.Remove(builder.Length - 2, 2).ToString(); },
            func: (builder, keyValuePair) =>
            {
                return builder.Append(keyValuePair.Key)
                    .Append("=")
                    .Append(keyValuePair.Value)
                    .Append("; ");
            }));
    }

    internal WebHeaderCollection GetWebHeader()
    {
        var value = _webHeaderCollection["Connection"];
        if (string.IsNullOrEmpty(value))
            _webHeaderCollection.Add("Connection", _connection);

        value = _webHeaderCollection["Accept-Language"];
        if (string.IsNullOrEmpty(value))
            _webHeaderCollection.Add("Accept-Language", _acceptLanguage);

        value = _webHeaderCollection["User-Agent"];
        if (string.IsNullOrEmpty(value))
            _webHeaderCollection.Add("User-Agent", _userAgent);

        if (Method == HttpCode.HttpMethod.Post)
        {
            value = _webHeaderCollection["Content-Type"];

            if (string.IsNullOrEmpty(value))
                _webHeaderCollection.Add("Content-Type", _contentType);

            if (!string.IsNullOrEmpty(Content))
            {
                if (Content != null)
                    _webHeaderCollection.Add("Content-Length", Content.Length.ToString());
                else
                    _webHeaderCollection.Add("Content-Length", "0");
            }
            else
            {
                if (Raw != null)
                    _webHeaderCollection.Add("Content-Length", Raw.Length.ToString());
                else
                    _webHeaderCollection.Add("Content-Length", "0");
            }
        }

        _webHeaderCollection.Add("Host", Uri.Host);

        return _webHeaderCollection;
    }

    internal EndPoint GetRemoteEndPoint()
    {
        if (IPAddress.TryParse(Uri.Host, out IPAddress address))
            return new IPEndPoint(address, Uri.Port);

        return new DnsEndPoint(Uri.Host, Uri.Port, System.Net.Sockets.AddressFamily.InterNetwork);
    }

    internal SslStreamConnector GetSslStreamConnector()
    {
        if (Uri.Port != 443)
            return null;

        var security = new SecurityOptions
            { EnabledSslProtocols = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12 };

        security.LocalCertificateSelectionCallback += (object sender, string targetHost,
            X509CertificateCollection localCertificates, X509Certificate remoteCertificate,
            string[] acceptableIssuers) =>
        {
            return null;
        };

        security.RemoteCertificateValidationCallback += (object sender, X509Certificate certificate, X509Chain chain,
            SslPolicyErrors sslPolicyErrors) =>
        {
            return true;
        };

        return new SslStreamConnector(security);
    }
}